Skip to content

feat(consensus): Replace static ValidatorSet with dynamic L2 contract-based validator fetching#3011

Merged
t00ts merged 5 commits intomainfrom
t00ts/l2-validator-fetcher
Sep 22, 2025
Merged

feat(consensus): Replace static ValidatorSet with dynamic L2 contract-based validator fetching#3011
t00ts merged 5 commits intomainfrom
t00ts/l2-validator-fetcher

Conversation

@t00ts
Copy link
Contributor

@t00ts t00ts commented Sep 16, 2025

This PR replaces the static ValidatorSet used in consensus with a dynamic validator fetching system that can retrieve validators from an L2 contract at runtime. This enables the consensus system to work with validator sets that change over time based on on-chain state.

New L2ValidatorSetProvider implements the ValidatorSetProvider trait to fetch validators dynamically.

To keep things working with our current setup, it supports two modes:

  • Config-based: When validator_addresses is provided in config, it will use those addresses with random keys.
  • Contract-based: When no addresses are configured, it will fetch from L2 contract using validator_fetcher::get_validators_at_height()

The validator fetching logic is temporarily in a new crate validator-fetcher. This was done because I've also added an RPC method for us to conveniently test this whenever the spec is finalized. As mentioned in the TODO comments, the validator-fetcher crate and its RPC method will be removed once the final specification is available, and the functionality will be migrated directly into the consensus module of the pathfinder crate.

As a side note, I also updated our pathfinder_consensus::ValidatorSetProvider trait to allow for errors.

Here is the Cairo test contract I'm using in case you're curious, which btw is deployed here.

use starknet::ContractAddress;
use core::array::{Array, ArrayTrait};

/// Validator information structure
#[derive(Drop, Serde, starknet::Store)]
struct ValidatorInfo {
    address: ContractAddress,
    public_key: felt252,
    voting_power: u64,
}

/// Mock contract that returns validators at a given height
#[starknet::contract]
mod ConsensusValidators {
    use super::*;

    #[storage]
    struct Storage {}

    // Ed25519 public keys generated for testing (felt252-compatible)
    const VALIDATOR_1: ValidatorInfo = ValidatorInfo {
        address: 0x111.try_into().unwrap(),
        public_key: 0x3b2223a9c54386b0b4f0f6b58fc2ec1e3b7f21581397ee3c7f,
        voting_power: 1000,
    };
    const VALIDATOR_2: ValidatorInfo = ValidatorInfo {
        address: 0x222.try_into().unwrap(),
        public_key: 0xd7db502088c25c78f0dd01492f86878da27b8fd391b001ed,
        voting_power: 1500,
    };
    const VALIDATOR_3: ValidatorInfo = ValidatorInfo {
        address: 0x333.try_into().unwrap(),
        public_key: 0x43cbc327a0b331fd17e915196ac339321ffd3bcaa7308f49d2,
        voting_power: 2000,
    };
    const VALIDATOR_4: ValidatorInfo = ValidatorInfo {
        address: 0x444.try_into().unwrap(),
        public_key: 0x8474f2adcef8935233876183dce7bb9a9136f53c57e578a85c,
        voting_power: 1200,
    };

    /// Builds the rotated list for a given height.
    fn rotate(height: u64) -> Array<ValidatorInfo> {
        let mut out = ArrayTrait::new();

        let n: u64 = 4_u64;
        let start: u64 = height % n;

        let mut i: u64 = 0_u64;
        while i < n {
            let idx = (start + i) % n;
            match idx {
                0_u64 => out.append(VALIDATOR_1),
                1_u64 => out.append(VALIDATOR_2),
                2_u64 => out.append(VALIDATOR_3),
                _     => out.append(VALIDATOR_4),
            };
            i += 1_u64;
        }

        out
    }

    /// Returns validator addresses at a given height
    #[external(v0)]
    fn get_validators_at_height(self: @ContractState, height: u64) -> Array<ContractAddress> {
        let validators = rotate(height);
        let mut addresses = ArrayTrait::new();
        let span = validators.span();
        let mut i = 0;
        while i < span.len() {
            addresses.append(*span.at(i).address);
            i += 1;
        }
        addresses
    }

    /// Returns detailed validator information at a given height.
    #[external(v0)]
    fn get_validator_info_at_height(self: @ContractState, height: u64) -> Array<ValidatorInfo> {
        rotate(height)
    }
}

@t00ts t00ts requested a review from a team as a code owner September 16, 2025 09:55
@t00ts
Copy link
Contributor Author

t00ts commented Sep 17, 2025

See also: #2935

@t00ts t00ts force-pushed the t00ts/l2-validator-fetcher branch from bd226d4 to 67ddfea Compare September 22, 2025 03:59
Copy link
Member

@CHr15F0x CHr15F0x left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! :shipit:

@t00ts t00ts merged commit d10ee15 into main Sep 22, 2025
8 checks passed
@t00ts t00ts deleted the t00ts/l2-validator-fetcher branch September 22, 2025 07:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants